#region

using System.ComponentModel;
using System.Reflection;

using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.OpenApi.Models;

using Swashbuckle.AspNetCore.SwaggerGen;

#endregion

namespace Leo.Core.Swagger
{
    public class DescriptionDocumentFilter : IDocumentFilter
    {
        public void Apply(OpenApiDocument doc, DocumentFilterContext context)
        {
            SetControllerDescription(doc, context);
            SetActionDescription(doc, context);
            SetModelDescription(context);
        }

        /// <summary>
        ///     设置控制器的描述信息
        /// </summary>
        /// <param name="doc"></param>
        /// <param name="context"></param>
        private void SetControllerDescription(OpenApiDocument doc, DocumentFilterContext context)
        {
            if (doc.Tags == null)
            {
                doc.Tags = new List<OpenApiTag>();
            }

            foreach (ApiDescription? apiDescription in context.ApiDescriptions)
            {
                if (apiDescription.TryGetMethodInfo(out MethodInfo methodInfo) && methodInfo.DeclaringType != null)
                {
                    DescriptionAttribute? descAttr =
                        (DescriptionAttribute)Attribute.GetCustomAttribute(methodInfo.DeclaringType,
                            typeof(DescriptionAttribute));
                    if (descAttr == null || string.IsNullOrEmpty(descAttr.Description))
                    {
                        continue;
                    }

                    string controllerName = methodInfo.DeclaringType.Name;
                    controllerName = controllerName.Remove(controllerName.Length - 10);
                    if (doc.Tags.All(t => t.Name != controllerName))
                    {
                        doc.Tags.Add(new OpenApiTag { Name = controllerName, Description = descAttr.Description });
                    }
                }
            }
        }

        /// <summary>
        ///     设置方法的说明
        /// </summary>
        /// <param name="doc"></param>
        /// <param name="context"></param>
        private void SetActionDescription(OpenApiDocument doc, DocumentFilterContext context)
        {
            foreach (KeyValuePair<string, OpenApiPathItem> path in doc.Paths)
            {
                if (TryGetActionDescription(path.Key, context, out string description))
                {
                    if (path.Value.Operations == null || !path.Value.Operations.Any())
                    {
                        continue;
                    }

                    KeyValuePair<OperationType, OpenApiOperation>
                        operation = path.Value.Operations.FirstOrDefault();
                    operation.Value.Description = description;
                    operation.Value.Summary = description;
                }
            }
        }

        /// <summary>
        ///     设置模型属性描述信息
        /// </summary>
        /// <param name="context"></param>
        private void SetModelDescription(DocumentFilterContext context)
        {
            FieldInfo? pro =
                typeof(SchemaRepository).GetField("_reservedIds", BindingFlags.NonPublic | BindingFlags.Instance);
            if (pro == null)
            {
                return;
            }

            Dictionary<Type, string>? schemaTypes = (Dictionary<Type, string>)pro.GetValue(context.SchemaRepository);

            foreach (KeyValuePair<string, OpenApiSchema> schema in context.SchemaRepository.Schemas)
            {
                Type type = schemaTypes
                    .FirstOrDefault(m => m.Value.Equals(schema.Key, StringComparison.OrdinalIgnoreCase)).Key;
                if (!type.IsClass)
                {
                    continue;
                }

                PropertyInfo[] properties = type.GetProperties();
                foreach (PropertyInfo propertyInfo in properties)
                {
                    OpenApiSchema? propertySchema = schema.Value.Properties
                        .FirstOrDefault(m => m.Key.Equals(propertyInfo.Name, StringComparison.OrdinalIgnoreCase)).Value;
                    if (propertySchema == null)
                    {
                        continue;
                    }

                    DescriptionAttribute? descAttr =
                        (DescriptionAttribute)Attribute.GetCustomAttribute(propertyInfo,
                            typeof(DescriptionAttribute));
                    if (descAttr != null && !string.IsNullOrEmpty(descAttr.Description))
                    {
                        propertySchema.Title = descAttr.Description;
                    }
                }
            }
        }

        /// <summary>
        ///     获取说明
        /// </summary>
        private bool TryGetActionDescription(string path, DocumentFilterContext context, out string description)
        {
            foreach (ApiDescription? apiDescription in context.ApiDescriptions)
            {
                string apiPath = "/" + apiDescription.RelativePath.ToLower();
                if (!apiPath.Equals(path) || !apiDescription.TryGetMethodInfo(out MethodInfo methodInfo))
                {
                    continue;
                }

                DescriptionAttribute? descAttr =
                    (DescriptionAttribute)Attribute.GetCustomAttribute(methodInfo, typeof(DescriptionAttribute));
                if (descAttr == null || string.IsNullOrEmpty(descAttr.Description))
                {
                    continue;
                }

                description = descAttr.Description;
                return true;
            }

            description = "";
            return false;
        }
    }
}